﻿/* Copyright (c) 2015 Slava Naumenko - http://m4w.codeplex.com */

using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;
using Microsoft.SharePoint.Utilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web.Services;

namespace M4W.ASM.Services
{
    [WebService(Namespace = "M4W.ASM.Services")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.Web.Script.Services.ScriptService]
    [System.ComponentModel.ToolboxItem(false)]
    public class Query : System.Web.Services.WebService
    {
        public enum EnumResultType
        {
            Count = 1,
            Value = 2
        }

        [WebMethod]
        public string CrossList(string TargetList, string SourceList, string TargetReferenceField, string TargetResultField, EnumResultType ResultType, string Query)
        {
            string result = "";
            try
            {
                result = QueryCrossList.Invoke(TargetList, SourceList, TargetReferenceField, TargetResultField, ResultType.ToString(), Query);
            }
            catch (Exception ex)
            {
                result = ex.Message.ToString();
            }
            return result;
        }

        [WebMethod]
        public string ApprovalHistory(string Library, string HistoryList, string TargetList)
        {
            string result = "";
            try
            {
                result = QueryApprovalHistory.Invoke(Library, HistoryList, TargetList);
            }
            catch (Exception ex)
            {
                result = ex.Message.ToString();
            }
            return result;
        }

        public enum EnumAppend
        {
            Nothing = 1,
            ItemDisplay = 2,
            ItemEdit = 3,
            TaskForm = 4,
            TaskContent = 5,
            ContentTask = 6
        }

        public enum EnumSendTo
        {
            EnteredUsers = 1,
            SiteOwners = 2,
            AssignedTo = 3
        }

        [WebMethod]
        public string SendEmail(string EmailSubject, string EmailBody, string EmailFrom, string EmailTo, string EmailCc, EnumAppend Append, EnumSendTo SendTo, string List, string Query)
        {
            string result = "";
            try
            {
                result = QuerySendEmail.Invoke(EmailSubject, EmailBody, EmailFrom, EmailTo, EmailCc, Append.ToString(), SendTo.ToString(), List, Query);
            }
            catch (Exception ex)
            {
                result = ex.Message.ToString();
            }
            return result;
        }

        [WebMethod]
        public string StartWorkflow(string Workflow, string List, string Query)
        {
            string result = "";
            try
            {
                result = QueryStartWorkflow.Invoke(Workflow, List, Query);
            }
            catch (Exception ex)
            {
                result = ex.Message.ToString();
            }
            return result;
        }
    }

    // Invocations

    internal static class QueryCrossList
    {
        internal static string Invoke(string TargetList, string SourceList, string TargetReferenceField, string TargetResultField, string ResultType, string Query)
        {
            string result = "";

            HandleEventFiring handleEventFiring = new HandleEventFiring();
            handleEventFiring.DisableHandleEventFiring();

            string curl = Helper.GetCurrentUrl();

            try
            {
                using (SPSite site = new SPSite(curl))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        web.AllowUnsafeUpdates = true;

                        SPList targetListInstance = Helper.GetList(web, TargetList);
                        SPList sourceListInstance = Helper.GetList(web, SourceList);

                        if (targetListInstance == null || sourceListInstance == null)
                        {
                            result = "List";
                            if (targetListInstance == null) { result += " '" + TargetList + "'"; }
                            if (sourceListInstance == null) { result += " '" + SourceList + "'"; }
                            result += " not found.";
                        }
                        else
                        {
                            SPListItemCollection targetItems = targetListInstance.Items;

                            string refType = targetListInstance.Fields[TargetReferenceField].TypeAsString;

                            if (ResultType == "Count")
                            {
                                int c = 0;

                                foreach (SPListItem item in targetItems)
                                {
                                    SPQuery query = new SPQuery();
                                    query.Query = String.Format(Query, GetReferenceValue(item, TargetReferenceField, refType));
                                    query.ViewAttributes = "Scope='Recursive'";
                                    query.ViewFields = "<FieldRef Name='ID' />";
                                    query.ViewFieldsOnly = true;

                                    SPListItemCollection sourceItems = sourceListInstance.GetItems(query);

                                    int count = sourceItems.Count;
                                    if (count > 0) { c += 1; }

                                    if (item[TargetResultField] == null || Int16.Parse(item[TargetResultField].ToString()) != count)
                                    {
                                        item[TargetResultField] = count;
                                        item.SystemUpdate(false);
                                    }
                                }

                                result = c.ToString();
                            }
                            else if (ResultType == "Value")
                            {
                                int u = 0;

                                string sourceResultField = TargetResultField;
                                string viewFields = "";

                                int vpos = Query.ToLower().IndexOf("<viewfields>");
                                if (vpos != -1)
                                {
                                    viewFields = Query.Substring(vpos + 12);
                                    viewFields = viewFields.Substring(0, viewFields.ToLower().IndexOf("</viewfields>"));

                                    if (viewFields != "")
                                    {
                                        string sourceResultFieldName = viewFields.Substring(viewFields.ToLower().IndexOf("='") + 2);
                                        sourceResultFieldName = sourceResultFieldName.Substring(0, sourceResultFieldName.ToLower().IndexOf("'"));

                                        sourceResultField = sourceListInstance.Fields.GetFieldByInternalName(sourceResultFieldName).Title;
                                    }

                                    Query = Query.Substring(0, Query.ToLower().IndexOf("<viewfields>"));
                                }

                                foreach (SPListItem item in targetItems)
                                {
                                    SPQuery query = new SPQuery();
                                    query.Query = String.Format(Query, GetReferenceValue(item, TargetReferenceField, refType));
                                    query.ViewAttributes = "Scope='Recursive'";
                                    if (viewFields != "")
                                    {
                                        query.ViewFields = viewFields;
                                        query.ViewFieldsOnly = true;
                                    }
                                    query.RowLimit = 1;

                                    SPListItemCollection sourceItems = sourceListInstance.GetItems(query);

                                    if (sourceItems.Count > 0)
                                    {
                                        SPListItem sourceItem = sourceItems[0];

                                        if (sourceItem[sourceResultField] != null && (item[TargetResultField] == null || item[TargetResultField].ToString() != sourceItem[sourceResultField].ToString()))
                                        {
                                            item[TargetResultField] = sourceItem[sourceResultField];
                                            item.SystemUpdate(false);

                                            u += 1;
                                        }
                                    }
                                }

                                result = u.ToString();
                            }
                        }

                        web.AllowUnsafeUpdates = false;
                    }
                }
            }
            catch (Exception ex)
            {
                result = ex.Message;
            }
            finally
            {
                handleEventFiring.EnableHandleEventFiring();
            }

            return result;
        }

        internal static string GetReferenceValue(SPListItem item, string field, string rtype)
        {
            string result = item[field].ToString();

            if (rtype == "TaxonomyFieldType")
            {
                result = ((TaxonomyFieldValue)item[field]).WssId.ToString();
            }

            return result;
        }
    }

    internal static class QueryApprovalHistory
    {
        internal static string Invoke(string Library, string HistoryList, string TargetList)
        {
            string result = "0";

            int docCount = 0;

            string curl = Helper.GetCurrentUrl();

            using (SPSite site = new SPSite(curl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    web.AllowUnsafeUpdates = true;

                    SPList libraryListInstance = Helper.GetList(web, Library);
                    SPList sourceListInstance = Helper.GetList(web, HistoryList);
                    SPList targetListInstance = Helper.GetList(web, TargetList);

                    if (libraryListInstance == null || sourceListInstance == null || targetListInstance == null)
                    {
                        result = "List or library";
                        if (libraryListInstance == null) { result += " '" + Library + "'"; }
                        if (sourceListInstance == null) { result += " '" + HistoryList + "'"; }
                        if (targetListInstance == null) { result += " '" + TargetList + "'"; }
                        result += " not found.";
                    }
                    else
                    {
                        // Find last processed workflow record

                        SPQuery query = new SPQuery();
                        query.Query = "<OrderBy><FieldRef Name='Occurred' Ascending='False'/></OrderBy>";
                        query.ViewFields = "<FieldRef Name='ID' /><FieldRef Name='Occurred' />";
                        query.ViewFieldsOnly = true;
                        query.RowLimit = 1;

                        SPListItemCollection itms = targetListInstance.GetItems(query);

                        string cutoff = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now.AddYears(-10));

                        if (itms.Count == 1) cutoff = SPUtility.CreateISO8601DateTimeFromSystemDateTime((DateTime)itms[0]["Occurred"]);

                        query = new SPQuery();
                        query.Query = "<Where><And><Gt><FieldRef Name='Occurred' /><Value IncludeTimeValue='TRUE' Type='DateTime'>" + cutoff + "</Value></Gt><Contains><FieldRef Name='List' /><Value Type='Text'>" + libraryListInstance.ID.ToString() + "</Value></Contains></And></Where><GroupBy collapse='true'><FieldRef Name='WorkflowInstance' /></GroupBy>";
                        SPListItemCollection items = sourceListInstance.GetItems(query);

                        // The above query filters items related to the given document library and groups them by workflow instance

                        SPListItem docItem = null;

                        int documentItemId = -1;
                        string documentName = "";
                        string workflowInstanceId = "";
                        string documentVersion = "";

                        SortedList<decimal, SPFileVersion> versions = new SortedList<decimal, SPFileVersion>();

                        foreach (SPListItem sourceItem in items)
                        {
                            // New workflow instance

                            if (workflowInstanceId != sourceItem["Workflow History Parent Instance"].ToString())
                            {
                                workflowInstanceId = sourceItem["Workflow History Parent Instance"].ToString();

                                if (documentItemId != (int)sourceItem["Primary Item ID"])
                                {
                                    documentItemId = (int)sourceItem["Primary Item ID"];
                                    versions.Clear();

                                    try
                                    {
                                        docItem = libraryListInstance.Items.GetItemById(documentItemId);
                                        documentName = docItem.File.Name;

                                        foreach (SPFileVersion version in docItem.File.Versions)
                                        {
                                            string[] versionArray = version.VersionLabel.Split('.');
                                            versions.Add(Convert.ToDecimal(versionArray[0]) * 100 + Convert.ToDecimal(versionArray[1]), version);
                                        }
                                    }
                                    catch (ArgumentException)
                                    {
                                        docItem = null;
                                        documentName = "";
                                    }
                                }

                                documentVersion = "N/A";

                                if (docItem != null)
                                {
                                    documentVersion = docItem.File.UIVersionLabel;

                                    foreach (KeyValuePair<decimal, SPFileVersion> version in versions.Reverse())
                                    {
                                        if ((DateTime)sourceItem["Date Occurred"] <= version.Value.Created.ToLocalTime()) { documentVersion = version.Value.VersionLabel; }
                                        else { break; }
                                    }
                                }

                                docCount += 1;
                            }

                            SPListItem targetItem = targetListInstance.AddItem();

                            targetItem["Doc Name"] = documentName;
                            targetItem["Doc Item Id"] = documentItemId;
                            targetItem["Doc Version"] = documentVersion;
                            targetItem["User"] = sourceItem["User ID"];
                            targetItem["Event"] = sourceItem["Event Type"];
                            targetItem["Date Occurred"] = sourceItem["Date Occurred"];
                            targetItem["Description"] = sourceItem["Description"];
                            targetItem["Outcome"] = sourceItem["Outcome"];

                            targetItem.Update();

                            result = docCount.ToString();
                        }
                    }

                    web.AllowUnsafeUpdates = false;
                }
            }

            return result;
        }
    }

    internal static class QuerySendEmail
    {
        internal static string Invoke(string EmailSubject, string EmailBody, string EmailFrom, string EmailTo, string EmailCc, string Append, string SendTo, string List, string Query)
        {
            string result = "";

            string[] appends = { "Nothing", "ItemDisplay", "ItemEdit", "TaskForm", "TaskContent", "ContentTask" };
            if (!appends.Contains(Append)) { throw new Exception("'" + Append + "' is not implemented."); }

            string curl = Helper.GetCurrentUrl();

            using (SPSite site = new SPSite(curl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    web.AllowUnsafeUpdates = true;

                    StringDictionary headers = new StringDictionary();

                    if (EmailFrom != "")
                    {
                        headers.Add("from", EmailFrom);
                    }

                    string aBody = "";
                    string fBody = "";
                    string aTitle = EmailSubject;

                    string tos = "";
                    switch (SendTo)
                    {
                        case "EnteredUsers":
                            if (EmailTo != "")
                            {
                                foreach (string s in EmailTo.Split(';'))
                                {
                                    if (s.Contains("@"))
                                    {
                                        tos += s + ";";
                                    }
                                    else
                                    {
                                        SPUser user = web.AllUsers[s];
                                        tos += user.Email + ";";
                                    }
                                }
                            }
                            else
                            {
                                throw new Exception("E-mail recipient is not set.");
                            }
                            break;
                        case "SiteOwners":
                            foreach (SPUser user in web.AllUsers)
                            {
                                if (user.IsSiteAdmin)
                                {
                                    tos += user.Email + ";";
                                }
                            }
                            break;
                        case "AssignedTo":
                            tos = "";
                            break;
                        default:
                            throw new Exception("'" + SendTo + "' is not implemented.");
                    }
                    headers.Add("to", tos);

                    if (EmailCc != "")
                    {
                        string ccs = "";
                        foreach (string c in EmailCc.Split(';'))
                        {
                            if (c.Contains("@"))
                            {
                                ccs += c + ";";
                            }
                            else
                            {
                                SPUser user = web.AllUsers[c.ToString()];
                                ccs += user.Email + ";";
                            }
                        }
                        if (ccs != "") { headers.Add("cc", ccs); }
                    }

                    SPList list = Helper.GetList(web, List);
                    if (list == null) { throw new Exception("List or library '" + List + "' not found."); }

                    SPQuery query = new SPQuery();
                    query.ViewAttributes = "Scope='Recursive'";
                    Helper.SetListQuery(query, Query);

                    SPListItemCollection items = list.GetItems(query);

                    headers.Add("subject", EmailSubject);

                    int e = 0;
                    foreach (SPListItem item in items)
                    {
                        string title = "";
                        if (query.ViewFields != null)
                        {
                            string[] fields = query.ViewFields.Split('>');
                            foreach (string f in fields)
                            {
                                string field = f;
                                if (field != "")
                                {
                                    field = field.Substring(field.IndexOf("=") + 1);
                                    field = field.Substring(0, field.IndexOf("/"));
                                    field = field.Trim().Replace("'", "").Replace("\"", "");

                                    if (field != "")
                                    {
                                        string val = "";
                                        if (item[field] != null) { val = item[field].ToString(); } else { val = "(no " + field.ToLower() + ")"; };
                                        if (val.Contains("|") && val.Contains("-")) { val = val.Substring(0, val.IndexOf("|")); }
                                        title += " - " + val;
                                    }
                                }
                            }
                            title = title.Substring(3);
                        }
                        else
                        {
                            title = item.Title; if (title == "") { title = "(no title)"; }
                        }

                        if (Append == "ItemDisplay")
                        {
                            string href = site.MakeFullUrl(list.DefaultDisplayFormUrl + "?ID=" + item.ID.ToString());
                            aTitle = EmailSubject + title;
                            aBody = "<a href='" + href + "'>" + title + "</a>";
                        }

                        if (Append == "ItemEdit")
                        {
                            string href = site.MakeFullUrl(list.DefaultEditFormUrl + "?ID=" + item.ID.ToString());
                            aTitle = EmailSubject + title;
                            aBody = "<a href='" + href + "'>" + title + "</a>";
                        }

                        if (Append == "TaskForm")
                        {
                            string href = web.Url + "/_layouts/WrkTaskIP.aspx?List=" + list.ID.ToString() + "&ID=" + item.ID.ToString();
                            aTitle = EmailSubject + title;
                            aBody = "<a href='" + href + "'>" + title + "</a>";
                        }

                        if (Append == "TaskContent" || Append == "ContentTask")
                        {
                            string href = web.Url + "/_layouts/WrkTaskIP.aspx?List=" + list.ID.ToString() + "&ID=" + item.ID.ToString();
                            aTitle = EmailSubject + title; aBody = "<a href='" + href + "'>" + title + "</a>";
                            SPField link = list.Fields.GetFieldByInternalName("WorkflowLink");
                            if (item[link.Title] != null)
                            {
                                string content = item[link.Title].ToString();
                                string cref = content.Substring(0, content.IndexOf(","));
                                string cname = content.Substring(content.IndexOf(",") + 1);
                                if (Append == "TaskContent") { aTitle = EmailSubject + title; aBody = "<a href='" + href + "'>" + title + "</a> related to <a href='" + cref + "'>" + cname + "</a>"; }
                                if (Append == "ContentTask") { aTitle = EmailSubject + cname; aBody = "<a href='" + cref + "'>" + cname + "</a> related task <a href='" + href + "'>" + title + "</a>"; }
                            }
                        }

                        if (SendTo == "AssignedTo")
                        {
                            SPField field = list.Fields.GetFieldByInternalName("AssignedTo");

                            bool isMulti = false;
                            if (field.TypeAsString.Equals("UserMulti")) { isMulti = true; }

                            if (isMulti)
                            {
                                foreach (SPFieldUserValue userValue in (SPFieldUserValueCollection)item[field.Title])
                                {
                                    SPUser user = userValue.User;

                                    tos += user.Email + ";";
                                }
                            }
                            else
                            {
                                SPFieldUserValue userField = (SPFieldUserValue)item.Fields[field.Title].GetFieldValue(item[field.Title].ToString());
                                SPUser user = userField.User;

                                tos = user.Email;
                            }

                            headers.Remove("subject");
                            headers.Add("subject", aTitle);

                            headers.Remove("to");
                            headers.Add("to", tos);
                        }

                        if (SendTo == "AssignedTo")
                        {
                            EmailSend.Mail(web, headers, EmailText.Append(EmailBody, aBody));
                        }
                        else
                        {
                            if (Append != "Nothing")
                            {
                                fBody += aBody + "<br/>\n\r";
                            }
                        }
                        e += 1;
                    }

                    if (e > 0 && (SendTo == "EnteredUsers" || SendTo == "SiteOwners"))
                    {
                        EmailSend.Mail(web, headers, EmailText.Append(EmailBody, fBody));
                        e = 1;
                    }

                    result = e.ToString();

                    web.AllowUnsafeUpdates = false;
                }
            }

            return result;
        }
    }

    internal static class QueryStartWorkflow
    {
        internal static string Invoke(string Workflow, string List, string Query)
        {
            string result = "";

            string curl = Helper.GetCurrentUrl();

            using (SPSite site = new SPSite(curl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    web.AllowUnsafeUpdates = true;

                    SPList list = Helper.GetList(web, List);

                    if (list == null)
                    {
                        result = "List or library '" + List + "' not found.";
                    }
                    else
                    {
                        SPQuery query = new SPQuery();
                        query.ViewAttributes = "Scope='Recursive'";
                        Helper.SetListQuery(query, Query);

                        SPListItemCollection items = list.GetItems(query);

                        int s = 0; int r = 0; int e = 0;
                        foreach (SPListItem item in items)
                        {
                            string workflowMessage = ListWorkflowStart.Invoke(Workflow, List, item.ID.ToString());
                            if (workflowMessage.Contains("already running"))
                            {
                                r += 1;
                            }
                            else if (workflowMessage.Contains("started"))
                            {
                                s += 1;
                            }
                            else
                            {
                                e += 1;
                            }
                        }

                        result = "Started: " + s.ToString() + "; Running: " + r.ToString() + "; Errored: " + e.ToString() + ".";
                    }

                    web.AllowUnsafeUpdates = false;
                }
            }

            return result;
        }
    }
}